package com.martini.satoken01.controller;

import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.crypto.digest.BCrypt;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import com.martini.satoken01.anno.AccessLimited;
import com.martini.satoken01.constant.ResCodeConstant;
import com.martini.satoken01.entity.SysUser;
import com.martini.satoken01.enums.LimitType;
import com.martini.satoken01.model.AjaxJson;
import com.martini.satoken01.model.domain.OnlineInfoDO;
import com.martini.satoken01.model.domain.QrLoginInfoDO;
import com.martini.satoken01.model.vo.LoginInfoVO;
import com.martini.satoken01.model.vo.LoginUserInfoVO;
import com.martini.satoken01.service.LoginInfoCacheService;
import com.martini.satoken01.service.system.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * 登录相关接口
 *
 * @author martini at 2021/12/16 16:11
 */
@Slf4j
@RestController
@RequestMapping("/login")
public class LoginController {

    private final LoginInfoCacheService loginInfoCacheService;

    private final UserService userService;

    public LoginController(LoginInfoCacheService loginInfoCacheService, UserService userService) {
        this.loginInfoCacheService = loginInfoCacheService;
        this.userService = userService;
    }

    @PostMapping
    @AccessLimited(limitDuration = 60 * 1000, limitTimes = 5, limitType = LimitType.IP, limitAfterLogin = false, limitInjectRemainTimesAttr = "remainTimes")
    public AjaxJson handleLogin(@RequestHeader("User-Agent") String uaStr,
                                @RequestBody Map<String, String> params,
                                @RequestParam(required = false) Integer remainTimes) {

        if (StpUtil.isLogin()) {
            return AjaxJson.fail().setCode(ResCodeConstant.ALREADY_LOGIN).setMsg("已经登录");
        }
        if (StringUtils.isBlank(params.get("account")) || StringUtils.isBlank(params.get("account"))) {
            return AjaxJson.fail().setMsg("账号密码不能为空");
        }

        String account = params.get("account");
        String password = params.get("password");

        log.info("account: {}, password: {}", account, password);

        SysUser user = null;
        if (StringUtils.contains(account, '@')) {
            user = userService.findUserByEmail(account);
        } else {
            user = userService.findUserByUsername(account);
        }

        if (!BCrypt.checkpw(password, user.getPassword())) {
            return AjaxJson.fail().setMsg("账号或者密码错误，请重试");
        }
        UserAgent ua = UserAgentUtil.parse(uaStr);
        String device = ua.getPlatform().toString();
        StpUtil.login(user.getId(), device);

        // 设置在线状态
        OnlineInfoDO onlineInfoDO = new OnlineInfoDO()
            .setLoginId(StpUtil.getLoginIdAsLong())
            .setDevice(device)
            .setLoginTime(LocalDateTime.now());
        userService.online(onlineInfoDO);

        // 更新最后登录时间
        SysUser sysUser = new SysUser();
        sysUser.setId(user.getId()).setLastLoginTime(LocalDateTime.now());
        userService.updateByPrimaryKeySelective(sysUser);

        // 设置登录信息
        LoginInfoVO loginInfoVO = new LoginInfoVO();
        loginInfoVO.setTokenInfo(StpUtil.getTokenInfo());
        loginInfoVO.setUserInfo(new LoginUserInfoVO().setNickname(user.getNickname()).setAvatar(user.getAvatar()));

        return AjaxJson.success().setData(loginInfoVO).setMsg("登录成功");

    }

    @GetMapping
    public AjaxJson isLogin() {
        return AjaxJson.success().setMsg("Session login: " + StpUtil.isLogin());
    }

    /**
     * 更新qr缓存中此key的用户信息
     *
     * @param key qr缓存key
     * @return {@link AjaxJson}
     */
    @PostMapping("/qr")
    @SaCheckLogin
    public AjaxJson updateQrInfo(@RequestHeader("User-Agent") String uaStr,
                                 @RequestParam String key) {
        long loginId = Long.parseLong(Objects.requireNonNull(StpUtil.getLoginId()).toString());

        SysUser sysUser = userService.findUserById(loginId);

        UserAgent ua = UserAgentUtil.parse(uaStr);

        QrLoginInfoDO qrLoginInfoDO = loginInfoCacheService.get(UserService.getQrLoginInfoKey(key), QrLoginInfoDO.class);
        // 已经被扫描
        qrLoginInfoDO.setScanned(true);
        // 不能立即登录，需要手机端再次确认
        qrLoginInfoDO.setLoginPermitted(false);
        qrLoginInfoDO.setAvatar(sysUser.getAvatar());
        qrLoginInfoDO.setNickname(sysUser.getNickname());
        qrLoginInfoDO.setDevice(ua.getPlatform().toString());
        qrLoginInfoDO.setSubjectId(loginId);

        loginInfoCacheService.put(UserService.getQrLoginInfoKey(key), qrLoginInfoDO);

        Map<String, Object> data = new HashMap<>(2);
        data.put("waitingDevice", qrLoginInfoDO.getWaitingDevice());
        data.put("key", key);
        return AjaxJson.success().setData(data);
    }

    /**
     * 通过key获取qr缓存中的值，如果值为空，说明移动端还没有扫码
     *
     * @return {@link AjaxJson}
     */
    @GetMapping("/qrCheck")
    public AjaxJson getQrCheckInfo(@RequestParam String key) {
        return AjaxJson.success().setData(loginInfoCacheService.get(UserService.getQrLoginInfoKey(key)));
    }

    @PostMapping("/byQr")
    public AjaxJson loginByQr(@RequestParam String key) {
        QrLoginInfoDO qrLoginInfoDO = loginInfoCacheService.get(UserService.getQrLoginInfoKey(key), QrLoginInfoDO.class);
        String waitingDevice = qrLoginInfoDO.getWaitingDevice();
        Object subjectId = qrLoginInfoDO.getSubjectId();
        StpUtil.login(subjectId, waitingDevice);

        OnlineInfoDO onlineInfoDO = new OnlineInfoDO()
            .setLoginId(StpUtil.getLoginIdAsLong())
            .setDevice(waitingDevice)
            .setLoginTime(LocalDateTime.now());
        userService.online(onlineInfoDO);

        // 设置登录信息
        LoginInfoVO loginInfoVO = new LoginInfoVO();
        loginInfoVO.setTokenInfo(StpUtil.getTokenInfo());
        loginInfoVO.setUserInfo(new LoginUserInfoVO()
                                    .setNickname(qrLoginInfoDO.getNickname())
                                    .setAvatar(qrLoginInfoDO.getAvatar()));

        return AjaxJson.success().setData(loginInfoVO).setMsg("登录成功");
    }

    @PostMapping("/qrLoginPermit")
    @SaCheckLogin
    public AjaxJson qrLoginPermit(@RequestParam String key) {
        QrLoginInfoDO qrLoginInfoDO = loginInfoCacheService.get(UserService.getQrLoginInfoKey(key), QrLoginInfoDO.class);
        qrLoginInfoDO.setLoginPermitted(true);
        loginInfoCacheService.put(UserService.getQrLoginInfoKey(key), qrLoginInfoDO);
        return AjaxJson.success().setMsg("授权成功");
    }
}
